package rbac

import (
	"context"
	"errors"
	"regexp"
	"sync"

	"gitee.com/go-mod/framework/util"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

// RBAC规则表
type RbacRule struct {
	ID   uint64 `gorm:"column:id" json:"id"`
	Type string `gorm:"column:type" json:"type"`
	V0   string `gorm:"column:v0" json:"v0"`
	V1   string `gorm:"column:v1" json:"v1"`
}

type Rbac struct {
	mu    *sync.RWMutex
	mysql *gorm.DB
	table string
	pMap  map[string][]string
	gMap  map[string][]string
	p2Map map[string]string
	g2Map map[string]string
}

var (
	instance *Rbac
	once     *sync.Once = &sync.Once{}
)

func NewRbac(gorm *gorm.DB, table string) *Rbac {
	once.Do(func() {
		instance = &Rbac{
			mu:    &sync.RWMutex{},
			mysql: gorm,
			table: table,
		}
		instance.load()
	})
	return instance
}

func (r *Rbac) load() {
	r.pMap = make(map[string][]string)
	r.gMap = make(map[string][]string)
	r.p2Map = make(map[string]string)
	r.g2Map = make(map[string]string)

	rules := []*RbacRule{}
	r.mysql.Table(r.table).Find(&rules)
	for _, v := range rules {
		if v.Type == "p" {
			r.pMap[v.V0] = append(r.pMap[v.V0], v.V1)
		}
		if v.Type == "g" {
			r.gMap[v.V0] = append(r.gMap[v.V0], v.V1)
		}
		if v.Type == "p2" {
			r.p2Map[v.V0] = v.V1
		}
		if v.Type == "g2" {
			r.g2Map[v.V0] = v.V1
		}
	}
}

// En 判断权限
func (r *Rbac) En(sub string, obj string, status string) bool {
	r.mu.RLock()
	defer r.mu.RUnlock()

	ps := r.getAllP(sub, status)
	for _, v := range ps {
		if v == obj {
			return true
		}
	}
	for _, v := range ps {
		if v[0:1] == "^" {
			if re, err := regexp.Compile(v); err == nil {
				if re.MatchString(obj) {
					return true
				}
			}
		}
	}
	return false
}

// GetP 获取主体权限
func (r *Rbac) GetP(sub string, status string) []string {
	r.mu.RLock()
	defer r.mu.RUnlock()

	return r.getP(sub, status)
}

func (r *Rbac) getP(sub string, status string) []string {
	perms := []string{}
	for _, v := range r.pMap[sub] {
		if status != "" && r.p2Map[v] != status {
			continue
		}
		perms = append(perms, v)
	}
	return perms
}

// GetAllP 获取主体权限 包含隐式
func (r *Rbac) GetAllP(sub string, status string) []string {
	r.mu.RLock()
	defer r.mu.RUnlock()

	return r.getAllP(sub, status)
}

func (r *Rbac) getAllP(sub string, status string) []string {
	// 主体权限
	perms := r.getP(sub, status)

	// 继承权限
	roles := r.getAllG(sub, status)
	for _, v := range roles {
		perms = append(perms, r.getP(v, status)...)
	}
	return util.RemoveDuplicates(perms)
}

// GetG 获取主体继承
func (r *Rbac) GetG(sub string, status string) []string {
	r.mu.RLock()
	defer r.mu.RUnlock()

	return r.getG(sub, status)
}

func (r *Rbac) getG(sub string, status string) []string {
	roles := []string{}
	for _, v := range r.gMap[sub] {
		if status != "" && r.g2Map[v] != status {
			continue
		}
		roles = append(roles, v)
	}
	return roles
}

// GetAllG 获取主体继承 包含隐式
func (r *Rbac) GetAllG(sub string, status string) []string {
	r.mu.RLock()
	defer r.mu.RUnlock()

	return r.getAllG(sub, status)
}

func (r *Rbac) getAllG(sub string, status string) []string {
	makeMap := make(map[string]struct{})
	return r.makeAllG(sub, status, makeMap)
}

func (r *Rbac) makeAllG(sub string, status string, makeMap map[string]struct{}) []string {
	makeMap[sub] = struct{}{}
	roles := []string{}
	for _, v := range r.gMap[sub] {
		if _, ok := makeMap[v]; !ok {
			if status != "" && r.g2Map[v] != status {
				continue
			}
			subs := r.makeAllG(v, status, makeMap)
			roles = append(roles, v)
			roles = append(roles, subs...)
		}
	}
	return roles
}

// UpdateObj 更新权限值
func (r *Rbac) UpdateObj(old string, new string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	if old == "" || new == "" {
		return false, errors.New("obj 不能为空")
	}

	errP := r.mysql.Table(r.table).Where("`type` = ? and `v1` = ?", "p", old).Update("v1", new).Error
	if errP != nil {
		return false, errP
	}
	errP2 := r.mysql.Table(r.table).Where("`type` = ? and `v0` = ?", "p2", old).Update("v0", new).Error
	if errP2 != nil {
		return false, errP2
	}

	r.load()
	return true, nil
}

// SetObjStatus 设置权限状态 已存在的更新 不存在的新增
func (r *Rbac) SetObjStatus(v0 []string, status string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.setStatus("p2", v0, status)
	r.load()
	return true, nil
}

// SetSubStatus 设置主体状态 已存在的更新 不存在的新增
func (r *Rbac) SetSubStatus(v0 []string, status string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.setStatus("g2", v0, status)
	r.load()
	return true, nil
}

func (r *Rbac) setStatus(t string, v0 []string, status string) (bool, error) {
	if len(v0) == 0 {
		return false, errors.New("v0 empty")
	}

	// 查询
	rules := []*RbacRule{}
	err := r.mysql.Table(r.table).Where("`type` = ? and `v0` in ?", t, v0).Find(&rules).Error
	if err != nil {
		return false, err
	}

	ruleMap := make(map[string]struct{})
	for _, v := range rules {
		ruleMap[v.V0] = struct{}{}
	}

	// 已存在的更新 不存在的新增
	updates := []string{}
	inserts := []*RbacRule{}
	for _, v := range v0 {
		if _, ok := ruleMap[v]; ok {
			updates = append(updates, v)
		} else {
			inserts = append(inserts, &RbacRule{
				Type: t,
				V0:   v,
				V1:   status,
			})
		}
	}

	if len(updates) > 0 {
		err := r.mysql.Table(r.table).Where("`type` = ? and `v0` in ?", t, updates).Update("v1", status).Error
		if err != nil {
			return false, err
		}
	}
	if len(inserts) > 0 {
		err := r.mysql.Table(r.table).Create(&inserts).Error
		if err != nil {
			return false, err
		}
	}

	return true, nil
}

// AddObjStatus 新增权限状态
func (r *Rbac) AddObjStatus(v0 []string, status string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.addStatus("p2", v0, status)
	r.load()
	return true, nil
}

// AddSubStatus 新增主体状态
func (r *Rbac) AddSubStatus(v0 []string, status string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.addStatus("g2", v0, status)
	r.load()
	return true, nil
}

func (r *Rbac) addStatus(t string, v0 []string, status string) (bool, error) {
	if len(v0) == 0 {
		return false, errors.New("v0 empty")
	}

	v0 = util.RemoveDuplicates(v0)
	ms := []*RbacRule{}
	for _, v := range v0 {
		ms = append(ms, &RbacRule{
			Type: t,
			V0:   v,
			V1:   status,
		})
	}

	// INSERT INTO `rbac_rule` (`type`,`v0`,`v1`) VALUES (?,?,?),(?,?,?) ON DUPLICATE KEY UPDATE `id`=`id`
	err := r.mysql.Table(r.table).Clauses(clause.OnConflict{DoNothing: true}).Create(&ms).Error
	if err != nil {
		return false, err
	}
	return true, nil
}

// UpdateObjStatus 更新权限状态
func (r *Rbac) UpdateObjStatus(v0 []string, status string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.updateStatus("p2", v0, status)
	r.load()
	return true, nil
}

// UpdateSubStatus 更新主体状态
func (r *Rbac) UpdateSubStatus(v0 []string, status string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.updateStatus("g2", v0, status)
	r.load()
	return true, nil
}

func (r *Rbac) updateStatus(t string, v0 []string, status string) (bool, error) {
	if len(v0) == 0 {
		return false, errors.New("v0 empty")
	}

	err := r.mysql.Table(r.table).Where("`type` = ? and `v0` in ?", t, v0).Update("v1", status).Error
	if err != nil {
		return false, err
	}
	return true, nil
}

// AddP 增量新增主体权限
func (r *Rbac) AddP(sub string, objs []string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.addRule("p", sub, objs)
	r.load()
	return true, nil
}

// AddG 增量新增主体继承
func (r *Rbac) AddG(sub string, subs []string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.addRule("g", sub, subs)
	r.load()
	return true, nil
}

func (r *Rbac) addRule(t string, sub string, rules []string) (bool, error) {
	if len(rules) == 0 {
		return true, nil
	}

	rules = util.RemoveDuplicates(rules)
	ms := []*RbacRule{}
	for _, v := range rules {
		ms = append(ms, &RbacRule{
			Type: t,
			V0:   sub,
			V1:   v,
		})
	}

	// INSERT INTO `rbac_rule` (`type`,`v0`,`v1`) VALUES (?,?,?),(?,?,?) ON DUPLICATE KEY UPDATE `id`=`id`
	err := r.mysql.Table(r.table).Clauses(clause.OnConflict{DoNothing: true}).Create(&ms).Error
	if err != nil {
		return false, err
	}
	return true, nil
}

// DeleteP 增量删除主体权限
func (r *Rbac) DeleteP(sub string, objs []string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.deleteRule("p", sub, objs)
	r.load()
	return true, nil
}

// DeleteG 增量删除主体继承
func (r *Rbac) DeleteG(sub string, subs []string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.deleteRule("g", sub, subs)
	r.load()
	return true, nil
}

func (r *Rbac) deleteRule(t string, sub string, rules []string) (bool, error) {
	if len(rules) == 0 {
		return true, nil
	}

	ctx := context.Background()
	err := r.mysql.Table(r.table).Where("`type` = ? and `v0` = ? and `v1` in ?", t, sub, rules).Delete(ctx).Error
	if err != nil {
		return false, err
	}
	return true, nil
}

// SetP 设置主体权限 删除再新增
func (r *Rbac) SetP(sub string, objs []string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.setRule("p", sub, objs)
	r.load()
	return true, nil
}

// SetG 设置主体继承 删除再新增
func (r *Rbac) SetG(sub string, subs []string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.setRule("g", sub, subs)
	r.load()
	return true, nil
}

func (r *Rbac) setRule(t string, sub string, rules []string) (bool, error) {
	// 删除
	ctx := context.Background()
	err := r.mysql.Table(r.table).Where("`type` = ? and `v0` = ?", t, sub).Delete(ctx).Error
	if err != nil {
		return false, err
	}

	return r.addRule(t, sub, rules)
}

// DeleteObj 删除权限
func (r *Rbac) DeleteObj(objs []string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	ctx := context.Background()
	err := r.mysql.Table(r.table).Where("`type` = ? and `v1` in ?", "p", objs).Delete(ctx).Error
	if err != nil {
		return false, err
	}
	err2 := r.mysql.Table(r.table).Where("`type` = ? and `v0` in ?", "p2", objs).Delete(ctx).Error
	if err2 != nil {
		return false, err2
	}

	r.load()
	return true, nil
}

// DeleteSub 删除主体
func (r *Rbac) DeleteSub(subs []string) (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	ctx := context.Background()
	errP := r.mysql.Table(r.table).Where("`type` = ? and `v0` in ?", "p", subs).Delete(ctx).Error
	if errP != nil {
		return false, errP
	}
	errG := r.mysql.Table(r.table).Where("`type` = ? and (`v0` in ? or `v1` in ?)", "g", subs, subs).Delete(ctx).Error
	if errG != nil {
		return false, errG
	}
	err2 := r.mysql.Table(r.table).Where("`type` = ? and `v0` in ?", "g2", subs).Delete(ctx).Error
	if err2 != nil {
		return false, err2
	}

	r.load()
	return true, nil
}

// ResetID 重置ID
func (r *Rbac) ResetID() (bool, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	ms := []*RbacRule{}
	for v0, v1s := range r.pMap {
		for _, v1 := range v1s {
			ms = append(ms, &RbacRule{
				Type: "p",
				V0:   v0,
				V1:   v1,
			})
		}
	}
	for v0, v1s := range r.gMap {
		for _, v1 := range v1s {
			ms = append(ms, &RbacRule{
				Type: "g",
				V0:   v0,
				V1:   v1,
			})
		}
	}
	for v0, v1 := range r.p2Map {
		ms = append(ms, &RbacRule{
			Type: "p2",
			V0:   v0,
			V1:   v1,
		})
	}
	for v0, v1 := range r.g2Map {
		ms = append(ms, &RbacRule{
			Type: "g2",
			V0:   v0,
			V1:   v1,
		})
	}

	errExec := r.mysql.Exec("truncate table `" + r.table + "`").Error
	if errExec != nil {
		return false, errExec
	}
	errCreate := r.mysql.Table(r.table).Create(&ms).Error
	if errCreate != nil {
		return false, errCreate
	}

	r.load()
	return true, nil
}
